#include "Client.h"

bool Client::SendTearDown()
{
    std::ostringstream auth;
    if(nonce.empty()) auth << authPre;
    else auth << authPre << ComputeRes("TEARDOWN") << "\"";

    sprintf(sendBuff, "TEARDOWN %s RTSP/1.0\r\n"
        "CSeq: %d\r\n"
        "User-Agent: %s\r\n"
        "Authorization: %s\r\n"
        "Session: %s\r\n"
        "\r\n",
        urlWithoutUser.c_str(), sendSeq++, userAgant.c_str(), auth.str().c_str(), session.c_str());
    send(rtspSocketFd, sendBuff, strlen(sendBuff), 0);
    
    bool getRes = false;
    for (size_t i = 0; i < WAIT_MESSAGE && !getRes; i++)
    {
        int bytesReceived = recv(rtspSocketFd, recvBuff, BUFF_SIZE, 0);
        if (bytesReceived <= 0)
        {
            std::cout << "TEARDOWN接收错误" << std::endl;
            return false;
        }

        char* line = strtok(recvBuff, sep);
        while (line)
        {
            if (strstr(line, "RTSP/1.0"))
            {
                if (sscanf(line, "RTSP/1.0 %d\r\n", &code) != 1)
                {
                    std::cout << "解析返回码失败" << std::endl;
                    return false;
                }

                if(code == 200) std::cout << "TEARDOWN指令执行完成" << std::endl;
                else std::cout << "TEARDOWN指令执行失败，状态码：" << code << std::endl;
                getRes = true;
                break;
            }

            line = strtok(NULL, sep);
        }
    }

    if(!getRes) std::cout << "未接收到TEARDOWN指令返回" << std::endl;

    return true;
}

bool Client::SendPause()
{
    std::ostringstream auth;
    if(nonce.empty()) auth << authPre;
    else auth << authPre << ComputeRes("PAUSE") << "\"";

    sprintf(sendBuff, "PAUSE %s RTSP/1.0\r\n"
        "CSeq: %d\r\n"
        "User-Agent: %s\r\n"
        "Authorization: %s\r\n"
        "Session: %s\r\n"
        "\r\n",
        urlWithoutUser.c_str(), sendSeq++, userAgant.c_str(), auth.str().c_str(), session.c_str());
    send(rtspSocketFd, sendBuff, strlen(sendBuff), 0);

    bool getRes = false;
    for (size_t i = 0; i < WAIT_MESSAGE && !getRes; i++)
    {
        int bytesReceived = recv(rtspSocketFd, recvBuff, BUFF_SIZE, 0);
        if (bytesReceived <= 0)
        {
            std::cout << "PAUSE接收错误" << std::endl;
            return false;
        }

        char* line = strtok(recvBuff, sep);
        while (line)
        {
            if (strstr(line, "RTSP/1.0"))
            {
                if (sscanf(line, "RTSP/1.0 %d\r\n", &code) != 1)
                {
                    std::cout << "解析返回码失败" << std::endl;
                    return false;
                }

                if(code == 200) std::cout << "PAUSE指令执行完成" << std::endl;
                else std::cout << "TEARDOWN指令执行失败，状态码：" << code << std::endl;
                getRes = true;
                break;
            }

            line = strtok(NULL, sep);
        }
    }

    if(!getRes) std::cout << "未接收到PAUSE指令返回" << std::endl;

    return true;
}

bool Client::ClientListen(int flvPort, const std::string& flvUrl)
{
    struct sockaddr_in address;
    int opt=1;
    int addlen = sizeof(address);
    char buffer[2000] = {0};

    if ((flvServerSocketFd = socket(AF_INET, SOCK_STREAM, 0))==0){
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    if(setsockopt(flvServerSocketFd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))){
        perror("setsoocketopt failed");
        exit(EXIT_FAILURE);
    }

    //配置服务器地址
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(flvPort);

    //绑定套接字
    if(bind(flvServerSocketFd, (struct sockaddr *)&address, addlen)<0){
        perror("bind failed");
        exit(EXIT_FAILURE);
    }

    //监听请求
    std::cout << "开始客户端监听，端口：" << flvPort << std::endl;
    std::cout << "5分钟内无客户端连接将自动停止转发程序。" << std::endl;
    if(listen(flvServerSocketFd, 3)<0){
        perror("listen failed");
        exit(EXIT_FAILURE);
    }

    while(true)
    {
        //接受客户端连接
        if((flvClientSocketFd = accept(flvServerSocketFd, (struct sockaddr *)&address, (socklen_t*)&addlen)) < 0){
            perror("accept failed");
            exit(EXIT_FAILURE);
        }
        if(read(flvClientSocketFd, buffer, 2000) <= 0)
        {
            std::cout << "http接收错误" << std::endl;
        }

        std::istringstream request(buffer);
        std::string method, path, version;
        request>> method >> path >> version;

        if(method=="GET")
        {
            if(path == flvUrl)
            {
                std::cout << "收到正确的url，开始推流" << std::endl;
                std::ostringstream response;  // 构建响应
                response <<"HTTP/1.1 200 OK\r\n"
                         <<"Access-Control-Allow-Origin: *" << "\r\n"
                         <<"Content-Type: vedio/x-flv" << "\r\n"
                         <<"Content-Length: -1" << "\r\n"
                         <<"Connection: Keep-Alive" << "\r\n"
                         <<"Expires: -1" << "\r\n"
                         <<"Pragma: no-cache" << "\r\n"
                         <<"\r\n";

                //发送响应
                send(flvClientSocketFd, response.str().c_str(), response.str().length(), 0);
                return true;
            }
            if(path == "/stop")
            {
                std::string OK="HTTP/1.1 200 OK\r\n";   
                send(flvClientSocketFd, OK.c_str(), OK.length(), 0);
                return false;
            }
            else
            {
                //404 NOT FOUND
                std::string notFound="HTTP/1.1 404 Not Found\r\n\r\n404 Not Found";
                send(flvClientSocketFd, notFound.c_str(), notFound.length(), 0);
            }
        }
        else
        {
            //405 METHOD NOT ALLOWED
            std::string notAllowed="HTTP/1.1 405 Method Not Allowed\r\n\r\n405 Method Not Allowed";
            send(flvClientSocketFd, notAllowed.c_str(), notAllowed.length(), 0);
        }

        //未跳出循环则关闭连接
        close(flvClientSocketFd);
    }
}

bool Client::SendPlay()
{
    std::ostringstream auth;
    if(nonce.empty()) auth << authPre;
    else auth << authPre << ComputeRes("PLAY") << "\"";

    sprintf(sendBuff, "PLAY %s RTSP/1.0\r\n"
        "CSeq: %d\r\n"
        "User-Agent: %s\r\n"
        "Authorization: %s\r\n"
        "Session: %s\r\n"
        "\r\n",
        urlWithoutUser.c_str(), sendSeq++, userAgant.c_str(), auth.str().c_str(), session.c_str());
    send(rtspSocketFd, sendBuff, strlen(sendBuff), 0);

    int bytesReceived = recv(rtspSocketFd, recvBuff, BUFF_SIZE, 0);
    if (bytesReceived <= 0)
    {
        std::cout << "PLAY接收错误" << std::endl;
        return false;
    }

    char* line = strtok(recvBuff, sep);
    while (line)
    {
        if (strstr(line, "RTSP/1.0"))
        {
            if (sscanf(line, "RTSP/1.0 %d\r\n", &code) != 1)
            {
                std::cout << "解析返回码失败" << std::endl;
                return false;
            }
        }

        line = strtok(NULL, sep);
    }

    if(code != 200)
    {
        std::cout << "rtsp连接失败：PLAY" << std::endl;
        return false;
    }
    std::cout << "rtsp连接成功，开始持续接收RTP数据" << std::endl;

    return true;
}

bool Client::SendSetup()
{
    std::ostringstream auth;
    if(nonce.empty()) auth << authPre;
    else auth << authPre << ComputeRes("SETUP") << "\"";

    sprintf(sendBuff, "SETUP %s/%s RTSP/1.0\r\n"
        "CSeq: %d\r\n"
        "User-Agent: %s\r\n"
        "Authorization: %s\r\n"
        "Transport: RTP/AVP/TCP;unicast;interleaved=0-1;mode=play\r\n"
        "\r\n",
        urlWithoutUser.c_str(), vedioTrack.c_str(), sendSeq++, userAgant.c_str(), auth.str().c_str());
    send(rtspSocketFd, sendBuff, strlen(sendBuff), 0);

    int bytesReceived = recv(rtspSocketFd, recvBuff, BUFF_SIZE, 0);
    if (bytesReceived <= 0)
    {
        std::cout << "SETUP接收错误" << std::endl;
        return false;
    }

    char* line = strtok(recvBuff, sep);
    char _session[20];
    while (line)
    {
        if (strstr(line, "RTSP/1.0"))
        {
            if (sscanf(line, "RTSP/1.0 %d\r\n", &code) != 1)
            {
                std::cout << "解析返回码失败" << std::endl;
                return false;
            }
        }
        else if (strstr(line, "Session"))
        {
            if (sscanf(line, "Session: %[^;]\r\n", _session) != 1)
            {
                std::cout << "解析Session失败" << std::endl;
                return false;
            }
        session = _session;
        }

        line = strtok(NULL, sep);
    }

    if(code != 200)
    {
        std::cout << "rtsp连接失败：SETUP" << std::endl;
        return false;
    }

    return true;
}

bool Client::SendDescribe()
{
    std::ostringstream auth;
    if(nonce.empty()) auth << authPre;
    else auth << authPre << ComputeRes("DESCRIBE") << "\"";
    
    sprintf(sendBuff, "DESCRIBE %s RTSP/1.0\r\n"
        "CSeq: %d\r\n"
        "User-Agent: %s\r\n"
        "Authorization: %s\r\n"
        "Accept: application/sdp\r\n"
        "\r\n",
        urlWithoutUser.c_str(), sendSeq++, userAgant.c_str(), auth.str().c_str());
    send(rtspSocketFd, sendBuff, strlen(sendBuff), 0);

    int bytesReceived = recv(rtspSocketFd, recvBuff, BUFF_SIZE, 0);
    if (bytesReceived <= 0)
    {
        std::cout << "DESCRIBE接收错误" << std::endl;
        return false;
    }
 
    char* line = strtok(recvBuff, sep);
    bool findVedio = false;
    bool isH264 = false;
    char _vedioTrack[20];
    vedioTrack = "";
    while (line)
    {
        if (strstr(line, "RTSP/1.0"))
        {
            if (sscanf(line, "RTSP/1.0 %d\r\n", &code) != 1)
            {
                std::cout << "解析返回码失败" << std::endl;
                return false;
            }
        }
        else if (strstr(line, "m=video")) findVedio = true;
        else if (strstr(line, "m=audio")) findVedio = false;
        else if (strstr(line, "a=control") && findVedio)
        {
            if (sscanf(line, "a=control:%s\r\n", _vedioTrack) != 1)
            {
                std::cout << "解析视频流track失败" << std::endl;
                return false;
            }
            vedioTrack = _vedioTrack;
        }
        else if (strstr(line, "a=rtpmap:96 H264") && findVedio) isH264 = true;
        else if (strstr(line, "a=framerate:") && findVedio)
        {
            if(sscanf(line, "a=framerate:%f\r\n", &frameRate) != 1)
            {
                std::cout << "解析视频流帧率" << std::endl;
                return false;
            }
        }

        line = strtok(NULL, sep);
    }
    
    if(!isH264)
    {
        std::cout << "当前客户端仅支持h264流" << std::endl;
        return false;
    }

    if(code != 200)
    {
        std::cout << "rtsp连接失败：DESCRIBE" << std::endl;
        return false;
    }

    return true;
}

bool Client::SendOption()
{
    sprintf(sendBuff, "OPTIONS %s RTSP/1.0\r\n"
	"CSeq: %d\r\n"
	"User-Agent: %s\r\n"
	"\r\n",
	urlWithoutUser.c_str(), sendSeq++, userAgant.c_str());

    send(rtspSocketFd, sendBuff, strlen(sendBuff), 0);

    int bytesReceived = recv(rtspSocketFd, recvBuff, BUFF_SIZE, 0);

    if (bytesReceived <= 0)
    {
        std::cout << "OPTIONS接收错误" << std::endl;
        return false;
    }
    
    char* line = strtok(recvBuff, sep);
    while (line)
    {
        if (strstr(line, "RTSP/1.0"))
        {
            if (sscanf(line, "RTSP/1.0 %d\r\n", &code) != 1)
            {
                std::cout << "解析返回码失败" << std::endl;
                return false;
            }
        }
        else if (strstr(line, "WWW-Authenticate"))
        {
            char _realm[50] = { 0 };
            char _nonce[50] = { 0 };
            if (sscanf(line, "WWW-Authenticate: Digest realm=\"%[^\"]\",nonce=\"%[^\"]\"\r\n", _realm, _nonce) != 2 
                && sscanf(line, "WWW-Authenticate: Basic realm=\"%[^\"]\"", _realm) != 1)
            {
                std::cout << "解析WWW-Authenticate失败" << std::endl;
                return false;
            }

            realm = _realm;
            nonce = _nonce;
        }

        line = strtok(NULL, sep);
    }

    if(code == 401)
    {
        std::ostringstream auth;
        if(nonce.empty())
        {
            std::string authSimple = Tools::EncodeBase64(userName + ":" + pwd);
            auth << "Basic " << authSimple;
            authPre = auth.str();
        }
        else
        {
            auth << "Digest username=\"" << userName << "\", "
            << "realm=\"" << realm << "\", "
            << "nonce=\"" << nonce << "\", "
            << "uri=\"" << urlWithoutUser << "\", "
            << "response=\"";
            
            authPre = auth.str();
            auth << ComputeRes("OPTIONS") << "\"";
        }
        
        sprintf(sendBuff, "OPTIONS %s RTSP/1.0\r\n"
            "CSeq: %d\r\n"
            "User-Agent: %s\r\n"
            "Authorization: %s\r\n"
            "\r\n",
            urlWithoutUser.c_str(), sendSeq++, userAgant.c_str(), auth.str().c_str());
        send(rtspSocketFd, sendBuff, strlen(sendBuff), 0);

        int bytesReceived = recv(rtspSocketFd, recvBuff, BUFF_SIZE, 0);
        if (bytesReceived > 0)
        {
            char* line = strtok(recvBuff, sep);
            while (line)
            {
                if (strstr(line, "RTSP/1.0"))
                {
                    if (sscanf(line, "RTSP/1.0 %d\r\n", &code) != 1)
                    {
                        std::cout << "解析返回码失败" << std::endl;
                        return false;
                    }
                }
                line = strtok(NULL, sep);
            }
        }
    }

    if(code != 200)
    {
        std::cout << "rtsp连接失败：OPTION" << std::endl;
        return false;
    }

    return true;
}

bool Client::ClientInit()
{
    struct sockaddr_in serv_addr;
 
    // 创建socket
    if ((rtspSocketFd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        std::cout << "套接字创建失败" << std::endl;
        return false;
    }
 
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(port);
    
    // 将IP地址从点分十进制转换为网络字节顺序的二进制形式
    if (inet_pton(AF_INET, IP.c_str(), &serv_addr.sin_addr) <= 0)
    {
        std::cout << "无效的网络地址" << std::endl;
        return false;
    }
 
    // 连接到服务器
    if (connect(rtspSocketFd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
    {
        std::cout << "服务端连接失败" << std::endl;
        return false;
    }

    return true;
}

bool Client::UrlParse(const char* url)
{
    if(strncmp(url, "rtsp://", 7) != 0)
    {
        std::cout << "流地址为非rtsp地址，无法解析" << std::endl;
        return false;
    }

    char _userName[40] = { 0 };
    char _pwd[40] = { 0 };
    char _IP[20] = { 0 };
    char _mediaRoute[100] = { 0 };
    char _urlWithoutUser[200] = { 0 };

    port = 554;
    if(sscanf(url + 7, "%[^:]:%[^@]@%[^:]:%d%s", _userName, _pwd, _IP, &port, _mediaRoute) == 5) //有账号密码，将账号密码移除
    {
        sprintf(_urlWithoutUser, "rtsp://%s:%d%s", _IP, port, _mediaRoute);
        urlWithoutUser = _urlWithoutUser;
        userName = _userName;
        pwd = _pwd;
        std::cout << "账号：" << userName << std::endl;
        std::cout << "密码：" << pwd << std::endl;
    }
    else if(sscanf(url + 7, "%[^:]:%d/%s", _IP, &port, _mediaRoute) == 3) urlWithoutUser = url;//无账号密码
    else if (sscanf(url + 7, "%[^/]/%s", _IP, _mediaRoute) == 2) urlWithoutUser = url;//无账号密码，无端口
    else
    {
        std::cout << "rtsp流地址解析失败" << std::endl;
        return false;
    }

    IP = _IP;
    std::cout << "流地址：" << urlWithoutUser << std::endl;

    return true;
}

void Client::StartConvert()
{
    DataNode* startNode = new DataNode(-1);
    flvWriter = new FlvWriter(flvTagData, frameRate, flvClientSocketFd, startNode);
    flvWriter->SetActive(true);
    flvThread = new std::thread(FlvWriter::FlvThread, flvWriter);
    rtpHandler = new RtpHandler(startNode);

    while (flvWriter->GetActive())
    {
        int recLength = MAX_REC_LENGTH < (REC_BUFFER_LENGTH_FLV - curBufferIndex) ? MAX_REC_LENGTH : (REC_BUFFER_LENGTH_FLV - curBufferIndex);
        bytesReceived = recv(rtspSocketFd, flvTagData + curBufferIndex, recLength, 0);
        if (bytesReceived <= 0) {
            std::cout << "rtp数据接收错误" << std::endl;
            break;
        }

        int latestIdrIndex = -1;
        if(!rtpHandler->RtpParser(flvTagData + curBufferIndex, bytesReceived, curBufferIndex, false, latestIdrIndex)) break;
        curBufferIndex += bytesReceived;
        if(curBufferIndex >= REC_BUFFER_LENGTH_FLV) curBufferIndex = 0;

        //追帧参考值
        if(latestIdrIndex != -1) flvWriter->SetLastestIDR(latestIdrIndex);
    }
}

void Client::TimerThread(int port)
{
    std::this_thread::sleep_for(std::chrono::seconds(300));

    struct sockaddr_in serv_addr;
    int socketFd = -1;
 
    //创建socket
    if((socketFd = socket(AF_INET, SOCK_STREAM, 0)) < 0) return;
 
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(port);
    
    //将IP地址从点分十进制转换为网络字节顺序的二进制形式
    if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) return;
 
    //连接到服务器
    if (connect(socketFd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) >= 0)
    {
        std::string stopMsg = "GET /stop HTTP/1.1";
        send(socketFd, stopMsg.c_str(), stopMsg.length(), 0);
        char temp[20];
        recv(socketFd, temp, 20, 0);
        std::cout << "长时间无客户端连接，程序结束。" << std::endl;
    }

    if(socketFd != -1) close(socketFd);
    return;
}

std::string Client::ComputeRes(const std::string& cmd)
{
    return Tools::ComputeMD5(Tools::ComputeMD5(userName + ":" + realm + ":" + pwd) + ":" + nonce + ":" + Tools::ComputeMD5(cmd + ":" + urlWithoutUser));
}

Client::Client(const char* _url, int _flvPort, const std::string& _flvUrl, bool& noError)
{
    curBufferIndex = 0;
    bytesReceived = 0;
    flvWriter = NULL;
    rtpHandler = NULL;

    noError = UrlParse(_url);
    if(!noError) return;

    flvServerSocketFd = -1;
    flvClientSocketFd = -1;
    std::thread timerThread(TimerThread, _flvPort);
    timerThread.detach();
    noError = ClientListen(_flvPort, _flvUrl);
    if(!noError) return;

    rtspSocketFd = -1;
    noError = ClientInit();
    if(!noError) return;

    //发起option请求
    userAgant = "kou3seki/rtspClient";
    sendSeq = 1;
    noError = SendOption();
    if(!noError) return;

    //发起describe请求
    noError = SendDescribe();
    if(!noError) return;

    //发起setup请求
    noError = SendSetup();
    if(!noError) return;

    //发起play请求
    noError = SendPlay();
    if(!noError) return;

    //开始持续处理rtp数据
    StartConvert();
}

Client::~Client()
{  
    if(flvWriter)
    {
        if(flvWriter->GetActive()) flvWriter->SetActive(false);
        flvThread->join();  
        delete flvThread;
        flvThread = NULL;

        delete flvWriter;
        flvWriter = NULL;
    }

    if(rtpHandler)
    {
        delete rtpHandler;
        rtpHandler = NULL;
    }

    SendPause();
    SendTearDown();
    std::cout << "------------------------------" << std::endl;
    std::cout << "-------RTSP播放结束-------" << std::endl;
    std::cout << "------------------------------" << std::endl << std::endl;
    if (rtspSocketFd != -1) close(rtspSocketFd);
    if(flvServerSocketFd != -1) close(flvServerSocketFd);
    if(flvClientSocketFd != -1) close(flvClientSocketFd);
}